﻿using MyGIS;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
using static MyGIS.GISShapefile;

namespace MyGIS
{


    public class GISVertex   //节点类
    {
        public double x;
        public double y;
        public GISVertex(double _x, double _y)
        {
            x = _x;
            y = _y;
        }//外部传参，向x与y赋值



        public GISVertex(BinaryReader br)//从文件中读取一个GISVertex实例
        {
            x = br.ReadDouble();
            y = br.ReadDouble();
        }
        public double Distance(GISVertex anothervertex)
        {
            return Math.Sqrt((x - anothervertex.x) *
                (x - anothervertex.x) + (y - anothervertex.y) * (y - anothervertex.y));
        }//计算两点间距离


        public void CopyFrom(GISVertex v)//使用输入点替换当前点
        {
            x = v.x;
            y = v.y;
        }


        /// <summary>
        /// 将点坐标输出到二进制文件
        /// </summary>
        /// <param name="bw"></param>
        public void WriteVertex(BinaryWriter bw)
        {
            bw.Write(x);
            bw.Write(y);
        }
    }


    public class GISPoint : GISSpatial  //点实体
    {

        public GISPoint(GISVertex onevertex)
        {
            centroid = onevertex;
            extent = new GISExtent(onevertex, onevertex);
        }//实例化点实体
        public override void draw(Graphics graphics, GISView view)
        {
            Point screenpoint = view.ToScreenPoint(centroid);
            graphics.FillEllipse(new SolidBrush(Color.Red),
                new Rectangle(screenpoint.X - 3, screenpoint.Y - 3, 6, 6));
        }//在屏幕上画出点实体
        public double Distance(GISVertex anothervertex)
        {
            return centroid.Distance(anothervertex);
        }//计算两点间距离
    }

    /// <summary>
    /// 线实体
    /// </summary>
    public class GISLine : GISSpatial
    {
        public List<GISVertex> Vertexs;
        public double Length;
        public GISLine(List<GISVertex> _vertexs)
        {
            if (_vertexs == null || _vertexs.Count < 2)
            {
                throw new ArgumentException("无效的输入节点");
            }
            Vertexs = _vertexs;
            centroid = GISTools.CalculateCentroid(_vertexs);
            extent = GISTools.CalculateExtent(_vertexs);
            Length = GISTools.CalculateLength(_vertexs);
        }
        public override void draw(Graphics graphics, GISView view)//画出Line
        {
            Point[] points = GISTools.GetScreenPoint(Vertexs, view);
            graphics.DrawLines(new Pen(Color.Red, 2), points);

        }
        public GISVertex FromNode()//返回起始结点
        {
            return Vertexs[0];
        }
        public GISVertex ToNode()//返回终止结点
        {
            return Vertexs[Vertexs.Count - 1];
        }
    }

    /// <summary>
    /// 面实体
    /// </summary>
    public class GISPolygon : GISSpatial
    {
        public List<GISVertex> Vertexs;
        public double Area;
        public GISPolygon(List<GISVertex> _vertexs)
        {
            if (_vertexs == null || _vertexs.Count < 3)//当输入节点数不符合要求时抛出异常
            {
                throw new ArgumentException("无效的输入节点");
            }
            Vertexs = _vertexs;
            centroid = GISTools.CalculateCentroid(_vertexs);
            Area = GISTools.CalculateArea(_vertexs);
            extent = GISTools.CalculateExtent(_vertexs);
        }
        public override void draw(Graphics graphics, GISView view)//画出Polygon
        {
            Point[] points = GISTools.GetScreenPoint(Vertexs, view);
            graphics.FillPolygon(new SolidBrush(Color.LightSteelBlue), points);
            graphics.DrawPolygon(new Pen(Color.White, 2), points);
        }
    }


    public class GISFeature   //空间要素特征类
    {
        public GISSpatial spatialpart;  //对象的空间信息
        public GISAttribute attributepart;  //对象的属性信息
        public GISFeature(GISSpatial spatial, GISAttribute attribute)
        {
            spatialpart = spatial;
            attributepart = attribute;
        }
        public void draw(Graphics graphics, GISView view, bool DrawAttributeOrNot, int index)
        {
            spatialpart.draw(graphics, view);
            if (DrawAttributeOrNot)
            {
                attributepart.draw(graphics, view, spatialpart.centroid, index);
            }
        }
        public object getAttribute(int index)
        {
            return attributepart.GetValue(index);
        }
    }


    public class GISAttribute
    {
        public ArrayList values = new ArrayList();
        public void AddValue(object o)  //添加属性值
        {
            values.Add(o);
        }
        public object GetValue(int index)  //获取属性值
        {
            return values[index];
        }
        public void draw(Graphics graphics, GISView view, GISVertex location, int index)
        {
            Point screenpoint = view.ToScreenPoint(location);
            graphics.DrawString(values[index].ToString(),
                new Font("宋体", 20),
                new SolidBrush(Color.Green),
                new PointF(screenpoint.X, screenpoint.Y));
        }//画出属性信息


        /// <summary>
        /// 返回属性字段的个数
        /// </summary>
        /// <returns></returns>
        public int ValueCount()
        {
            return values.Count;
        }
    }


    public abstract class GISSpatial  //抽象空间要素类
    {
        public GISVertex centroid;//空间实体的中心点
        public GISExtent extent;//空间范围：最小外接矩形
        public abstract void draw(Graphics graphics, GISView view);
    }

    /// <summary>
    /// 空间对象的范围
    /// </summary>
    public class GISExtent
    {
        public GISVertex bottomleft;
        public GISVertex upright;
        public double ZoomingFactor = 2;//缩放系数，放大或缩小时会缩放为2倍或二分之一
        public double MovingFactor = 0.25;//移动系数，即移动一次会有四分之一的屏幕范围移除屏幕

        /// <summary>
        /// 显示范围（左下角点，右上角点）
        /// </summary>
        /// <param name="_bottomleft"></param>
        /// <param name="_upright"></param>
        public GISExtent(GISVertex _bottomleft, GISVertex _upright)
        {
            bottomleft = _bottomleft;
            upright = _upright;
        }
        public double getMinX()
        {
            return bottomleft.x;
        }
        public double getMaxX()
        {
            return upright.x;
        }
        public double getMinY()
        {
            return bottomleft.y;
        }
        public double getMaxY()
        {
            return upright.y;
        }
        public double getWidth()  //计算地图横坐标长度
        {
            return upright.x - bottomleft.x;
        }
        public double getHeight()  //计算地图纵坐标长度
        {
            return upright.y - bottomleft.y;
        }
        public void ChangeExtent(GISMapActions actions)
        {
            double newminx = bottomleft.x, newminy = bottomleft.y,
                newmaxx = upright.x, newmaxy = upright.y;
            switch (actions)
            {
                case GISMapActions.zoomin:  //地图放大
                    newminx = ((getMinX() + getMaxX()) - getWidth() / ZoomingFactor) / 2;
                    newminy = ((getMinY() + getMaxY()) - getHeight() / ZoomingFactor) / 2;
                    newmaxx = ((getMinX() + getMaxX()) + getWidth() / ZoomingFactor) / 2;
                    newmaxy = ((getMinY() + getMaxY()) + getHeight() / ZoomingFactor) / 2;
                    break;
                case GISMapActions.zoomout:  //地图缩小
                    newminx = ((getMinX() + getMaxX()) - getWidth() * ZoomingFactor) / 2;
                    newminy = ((getMinY() + getMaxY()) - getHeight() * ZoomingFactor) / 2;
                    newmaxx = ((getMinX() + getMaxX()) + getWidth() * ZoomingFactor) / 2;
                    newmaxy = ((getMinY() + getMaxY()) + getHeight() * ZoomingFactor) / 2;
                    break;
                case GISMapActions.moveup:  //向上移动，即地图范围下移
                    newminy = getMinY() - getHeight() * MovingFactor;
                    newmaxy = getMaxY() - getHeight() * MovingFactor;
                    break;
                case GISMapActions.movedown:  //向下移动，即地图范围上移
                    newminy = getMinY() + getHeight() * MovingFactor;
                    newmaxy = getMaxY() + getHeight() * MovingFactor;
                    break;
                case GISMapActions.moveleft:  //向左移动
                    newminx = getMinX() + getWidth() * MovingFactor;
                    newmaxx = getMaxX() + getWidth() * MovingFactor;
                    break;
                case GISMapActions.moveright:  //向右移动
                    newminx = getMinX() - getWidth() * MovingFactor;
                    newmaxx = getMaxX() - getWidth() * MovingFactor;
                    break;
            }
            upright.x = newmaxx;
            upright.y = newmaxy;
            bottomleft.x = newminx;
            bottomleft.y = newminy;
        }

        public void CopyFrom(GISExtent extent)//使用Extent替换当前值
        {
            upright.CopyFrom(extent.upright);
            bottomleft.CopyFrom(extent.bottomleft);
        }
    }



    public class GISView   //空间对象浏览类
    {
        GISExtent CurrentMapExtent;//记录显示的地图范围
        Rectangle MapWindowSize;  //记录绘图窗口的大小 
        double MapMinX, MapMinY;
        int WinW, WinH;
        double MapW, MapH;
        double ScaleX, ScaleY;
        public GISView(GISExtent _extent, Rectangle _rectangle)
        {
            UpdateMap(_extent, _rectangle);
        }

        /// <summary>
        /// 更新地图
        /// </summary>
        /// <param name="_extent"></param>
        /// <param name="_rectangle"></param>
        public void UpdateMap(GISExtent _extent, Rectangle _rectangle)
        {
            CurrentMapExtent = _extent;//当前地图范围
            MapWindowSize = _rectangle;
            MapMinX = CurrentMapExtent.getMinX();//地图最小X值
            MapMinY = CurrentMapExtent.getMinY();
            WinW = MapWindowSize.Width;//窗口宽度
            WinH = MapWindowSize.Height;//窗口高度
            MapW = CurrentMapExtent.getWidth();
            MapH = CurrentMapExtent.getHeight();
            ScaleX = MapW / WinW;//比例尺=图上距离/实际距离
            ScaleY = MapH / WinH;
        }//更新地图

        public void UpdateExtent(GISExtent extent)//更新当前地图范围
        {
            CurrentMapExtent.CopyFrom(extent);
            UpdateMap(CurrentMapExtent, MapWindowSize);
        }

        public Point ToScreenPoint(GISVertex onevertex)//地图转屏幕点
        {
            double ScreenX = (onevertex.x - MapMinX) / ScaleX;
            double ScreenY = WinH - (onevertex.y - MapMinY) / ScaleY;
            return new Point((int)ScreenX, (int)ScreenY);
        }

        public GISVertex ToMapVertex(Point point)//屏幕转地图点
        {
            double MapX = ScaleX * point.X + MapMinX;
            double MapY = ScaleY * (WinH - point.Y) + MapMinY;
            return new GISVertex(MapX, MapY);
        }

        public void ChangeView(GISMapActions actions)//改变显示范围
        {
            CurrentMapExtent.ChangeExtent(actions);
            UpdateMap(CurrentMapExtent, MapWindowSize);
        }
    }

    public enum GISMapActions  //地图的缩放与平移操作
    {
        zoomin, zoomout,
        moveup, movedown, moveleft, moveright
    }

    class GISTools
    {
        public static GISVertex CalculateCentroid(List<GISVertex> _vertex)//计算空间实体中心点
        {
            if (_vertex.Count == 0) return null;
            double x = 0;
            double y = 0;
            for (int i = 0; i < _vertex.Count; i++)
            {
                x += _vertex[i].x;
                y += _vertex[i].y;
            }
            return new GISVertex(x / _vertex.Count, y / _vertex.Count);
        }
        public static GISExtent CalculateExtent(List<GISVertex> _vertex)//计算空间实体范围
        {
            if (_vertex.Count == 0) return null;
            double minx = Double.MaxValue;
            double miny = Double.MaxValue;
            double maxx = Double.MinValue;
            double maxy = Double.MinValue;
            for (int i = 0; i < _vertex.Count; i++)
            {
                if (_vertex[i].x < minx) minx = _vertex[i].x;
                if (_vertex[i].y < miny) miny = _vertex[i].y;
                if (_vertex[i].x > maxx) maxx = _vertex[i].x;
                if (_vertex[i].y > maxy) maxy = _vertex[i].y;
            }
            return new GISExtent(new GISVertex(minx, miny), new GISVertex(maxx, maxy));
        }

        public static double CalculateLength(List<GISVertex> _vertex)//计算线长度或面的周长（折线长度）
        {
            double length = 0;
            for (int i = 0; i < _vertex.Count - 1; i++)
            {
                length += _vertex[i].Distance(_vertex[i + 1]);
            }
            return length;
        }

        public static double CalculateArea(List<GISVertex> _vertex)//计算面实体的面积（利用矢量积计算）
        {
            double area = 0;
            for (int i = 0; i < _vertex.Count - 1; i++)
            {
                area += VectorProduct(_vertex[i], _vertex[i + 1]);
            }
            return area / 2;
        }

        public static double VectorProduct(GISVertex v1, GISVertex v2)//矢量积的计算
        {
            return v1.x * v2.y - v1.y * v2.x;
        }


        /// <summary>
        /// 把节点按当前的GISView转换成屏幕坐标
        /// </summary>
        /// <param name="_vertexs"></param>
        /// <param name="view"></param>
        /// <returns></returns>
        public static Point[] GetScreenPoint(List<GISVertex> _vertexs, GISView view)
        {
            Point[] points = new Point[_vertexs.Count];
            for (int i = 0; i < points.Length; i++)
            {
                points[i] = view.ToScreenPoint(_vertexs[i]);//对每个点调用ToScreenPoint方法
            }
            return points;
        }


        /// <summary>
        /// 将结构体转存为字节数组的方法
        /// </summary>
        /// <param name="c"></param>
        /// <returns></returns>
        public static byte[] ToBytes(object c)
        {
            byte[] bytes = new byte[Marshal.SizeOf(c.GetType())];
            GCHandle handle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
            Marshal.StructureToPtr(c, handle.AddrOfPinnedObject(), false);
            handle.Free();
            return bytes;
        }


        /// <summary>
        /// 写入自定义格式中的文件字符串
        /// </summary>
        /// <param name="s"></param>
        /// <param name="bw"></param>
        public static void WriteString(string s, BinaryWriter bw)
        {
            bw.Write(StringLength(s));//写入字节长度
            byte[] sbytes = Encoding.Default.GetBytes(s);//将字符串转换为字节数组
            bw.Write(sbytes);
        }


        /// <summary>
        /// 计算字符串中的字节数（因一个中文字符包含两个字节）
        /// </summary>
        /// <param name="s"></param>
        /// <returns></returns>
        public static int StringLength(string s)
        {
            int ChineseCount = 0;
            byte[] bs = new ASCIIEncoding().GetBytes(s);
            foreach (byte b in bs)
                if (b == 0X3F) ChineseCount++;//字符串转换为字节数组时中文字符会被转换为0X3F
            return ChineseCount + bs.Length;
        }


        /// <summary>
        /// 将给定的数据类型转换为整型
        /// </summary>
        /// <param name="type"></param>
        /// <returns></returns>
        public static int TypeToInt(Type type)
        {
            ALLTYPES onetype = (ALLTYPES)Enum.Parse(typeof(ALLTYPES), type.ToString().Replace(".", "_"));
            return (int)onetype;
        }


        /// <summary>
        /// 从文件中读取结构体的实例
        /// </summary>
        /// <param name="br"></param>
        /// <param name="type"></param>
        /// <returns></returns>
        public static Object FromBytes(BinaryReader br, Type type)
        {
            byte[] buff = br.ReadBytes(Marshal.SizeOf(type));//将与ShapefileHeader同样大小的字节赋给buff字符串
            GCHandle handle = GCHandle.Alloc(buff, GCHandleType.Pinned);//handle获取buff数组在内存中的指针
            Object result = Marshal.PtrToStructure(handle.AddrOfPinnedObject(), type);//该指针指向的内存被映射给ShapefileHeader结构体的实例header
            handle.Free();//释放指针
            return result;
        }


        /// <summary>
        /// 从文件中读取一个字符串
        /// </summary>
        /// <param name="br"></param>
        /// <returns></returns>
        public static string ReadString(BinaryReader br)
        {
            int length = br.ReadInt32();
            byte[] sbytes = br.ReadBytes(length);//读取字节数组
            return Encoding.Default.GetString(sbytes);//进行编码
        }


        /// <summary>
        /// 把从文件中读取到的整数转化为特定的数据类型
        /// </summary>
        /// <param name="index"></param>
        /// <returns></returns>
        public static Type IntToType(int index)
        {
            string typestring = Enum.GetName(typeof(ALLTYPES), index);
            typestring = typestring.Replace("_", ".");
            return Type.GetType(typestring);
        }
    }


    /// <summary>
    /// 记录各种数据类型
    /// </summary>
    public enum ALLTYPES
    {
        System_Boolean,//布尔型
        System_Byte,//无符号8位整数
        System_Char,//字符型
        System_Decimal,//有符号128位实数
        System_Double,//有符号64位实数
        System_Single,//有符号32位实数
        System_Int32,//有符号32位整数
        System_Int64,//有符号64位整数
        System_SByte,//有符号8位整数
        System_Int16,//有符号16位整数
        System_String,//字符串
        System_UInt32,//无符号32位整数
        System_UInt64,//无符号64位整数
        System_UInt16//无符号16位整数
    }

    public class GISLayer
    {
        public string Name;//图层名称
        public GISExtent Extent;//地图范围
        public bool DrawAttributeOrNot = false;//绘制图层时是否标注属性信息
        public int LabelIndex;//需要标注的属性序列号
        public GISShapefile.SHAPETYPE ShapeType;//空间对象类型
        List<GISFeature> Features = new List<GISFeature>();//用于记录该图层中包含的所有空间对象实体

        public List<GISField> Fields;
        public GISLayer(string _name, SHAPETYPE _shapefile, GISExtent _extent, List<GISField> _fields)
        {
            Name = _name;
            ShapeType = _shapefile;
            Extent = _extent;
            Fields = _fields;
        }
        public GISLayer(string _name, SHAPETYPE _shapetype, GISExtent _extent)
        {
            Name = _name;
            ShapeType = _shapetype;
            Extent = _extent;
            Fields = new List<GISField>();
        }
        public void draw(Graphics graphics, GISView view)
        {
            for (int i = 0; i < Features.Count; i++)
            {
                Features[i].draw(graphics, view, DrawAttributeOrNot, LabelIndex);
            }
        }
        public void AddFeature(GISFeature feature)//添加要素
        {
            Features.Add(feature);
        }
        public int FeatureCount()//用于获取Feature中元素的数量
        {
            return Features.Count;
        }
        public GISFeature GetFeature(int i)//获取图层中的i空间要素的属性值
        {
            return Features[i];
        }
    }

    /// <summary>
    /// 记录字段的数据类型和名称
    /// </summary>
    public class GISField
    {
        public Type datatype;
        public string name;
        public GISField(Type type, string name)
        {
            datatype = type;
            this.name = name;
        }
    }
}
